#pragma once

#ifdef __cplusplus
extern "C"
{
#endif

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>

#include "platform.h"

#ifndef NDEBUG

#ifndef __FILENAME__
#ifdef WIN32
#define __FILENAME__ (strrchr(__FILE__, '\\') + (1))
#else
#define __FILENAME__ (strrchr(__FILE__, '/') + (1))
#endif
#endif

#define LOG_PRINT(level, format, ...)                                                                                                 \
    do                                                                                                                                \
    {                                                                                                                                 \
        struct timeval log_tv = {};                                                                                                   \
        gettimeofday(&log_tv, NULL);                                                                                                  \
        time_t log_now = log_tv.tv_sec;                                                                                               \
        struct tm log_timeinfo = {};                                                                                                  \
        localtime_r(&log_now, &log_timeinfo);                                                                                         \
        char log_dateTime[128 * 2] = {};                                                                                              \
        char log_dateStr[128] = {};                                                                                                   \
        strftime(log_dateStr, sizeof(log_dateStr), "%Y-%m-%d %H:%M:%S", &log_timeinfo);                                               \
        snprintf(log_dateTime, sizeof(log_dateTime), "%s.%06ld", log_dateStr, log_tv.tv_usec);                                        \
        fprintf(stdout, "[%s %s] [%s:%d %s] " format "\n", log_dateTime, level, __FILENAME__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
    } while (0)

#define LOG_PRINT_CRIT(...)  LOG_PRINT("C", ##__VA_ARGS__)
#define LOG_PRINT_ERROR(...) LOG_PRINT("E", ##__VA_ARGS__)
#define LOG_PRINT_WARN(...)  LOG_PRINT("W", ##__VA_ARGS__)
#define LOG_PRINT_INFO(...)  LOG_PRINT("I", ##__VA_ARGS__)
#define LOG_PRINT_DEBUG(...) LOG_PRINT("D", ##__VA_ARGS__)

#define LOG_PRINT_BUF(name, buf, length)                                                          \
    do                                                                                            \
    {                                                                                             \
        size_t _len = (length);                                                                   \
        const uint8_t *_tmp_buff = (const uint8_t *)(buf);                                        \
        const uint8_t _BYTES_PER_COL = 32;                                                        \
        char _row_buff[32 * 3 + 1] = {0};                                                         \
        size_t _start_byte = 0;                                                                   \
        size_t _row_cnt = (_len) / _BYTES_PER_COL;                                                \
        size_t _last_bytes = (_len) % _BYTES_PER_COL;                                             \
        size_t _row_bytes = 0;                                                                    \
                                                                                                  \
        if (_last_bytes)                                                                          \
            _row_cnt++;                                                                           \
        LOG_PRINT_DEBUG("Buffer[%s] start: total{bytes[%zu],rows[%zu]}\n", name, _len, _row_cnt); \
                                                                                                  \
        for (size_t __i = 0; __i < _row_cnt; ++__i)                                               \
        {                                                                                         \
            memset(_row_buff, 0, _BYTES_PER_COL * 3);                                             \
            _start_byte = __i * _BYTES_PER_COL;                                                   \
            _row_bytes = (_last_bytes && (__i == (_row_cnt - 1))) ? _last_bytes : _BYTES_PER_COL; \
                                                                                                  \
            for (size_t __j = 0; __j < _row_bytes; ++__j)                                         \
            {                                                                                     \
                snprintf(&_row_buff[__j * 3], 4, "%02X ", _tmp_buff[_start_byte + __j]);          \
            }                                                                                     \
                                                                                                  \
            LOG_PRINT_DEBUG("[%03zu] %s\n", __i, _row_buff);                                      \
        }                                                                                         \
                                                                                                  \
        LOG_PRINT_DEBUG("Buffer[%s] end.\n", name);                                               \
    } while (0)
#else
#define LOG_PRINT(level, format, ...)                                   \
    do                                                                  \
    {                                                                   \
        if (0)                                                          \
        {                                                               \
            fprintf(stdout, "[%s] " format "\n", level, ##__VA_ARGS__); \
        }                                                               \
    } while (0)

#define LOG_PRINT_CRIT(...)  LOG_PRINT("C", __VA_ARGS__)
#define LOG_PRINT_ERROR(...) LOG_PRINT("E", __VA_ARGS__)
#define LOG_PRINT_WARN(...)  LOG_PRINT("W", __VA_ARGS__)
#define LOG_PRINT_INFO(...)  LOG_PRINT("I", __VA_ARGS__)
#define LOG_PRINT_DEBUG(...) LOG_PRINT("D", __VA_ARGS__)

#define LOG_PRINT_BUF(name, buf, length) \
    do                                   \
    {                                    \
        if (0)                           \
        {                                \
            (void)(name);                \
            (void)(buf);                 \
            (void)(length);              \
        }                                \
    } while (0)

#endif

#ifndef likely
#if defined(__GNUC__) || defined(__clang__)
#define likely(cond)   __builtin_expect(!!(cond), 1)
#define unlikely(cond) __builtin_expect(!!(cond), 0)
#else
#define likely(cond)   (!!(cond))
#define unlikely(cond) (!!(cond))
#endif
#endif

#ifndef UNUSED
#define UNUSED(x) (void)(x)
#endif

#ifndef TOSTR
#define _TOSTR(x) #x
#define TOSTR(x)  _TOSTR(x)
#endif

#ifndef CONN
#define _CONN(x, y) x##y
#define CONN(x, y)  _CONN(x, y)
#endif

#define _ARGS_GET_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N
#define ARGS_COUNT_NO_EMPTY(...)                                                                   __VA_OPT__(_ARGS_GET_N(__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1))
#define ARGS_COUNT(...)                                                                            (0 __VA_OPT__(+ARGS_COUNT_NO_EMPTY(__VA_ARGS__)))

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif

#ifndef MIN
#define MIN(x, y)                      \
    ({                                 \
        typeof(x) _min1 = (x);         \
        typeof(y) _min2 = (y);         \
        (void)(&_min1 == &_min2);      \
        _min1 < _min2 ? _min1 : _min2; \
    })
#endif

#ifndef MAX
#define MAX(x, y)                      \
    ({                                 \
        typeof(x) _max1 = (x);         \
        typeof(y) _max2 = (y);         \
        (void)(&_max1 == &_max2);      \
        _max1 > _max2 ? _max1 : _max2; \
    })
#endif

#ifndef SWAP
#define SWAP(x, y)            \
    ({                        \
        typeof(x) _tmp = (x); \
        (x) = (y);            \
        (y) = _tmp;           \
    })
#endif

#ifndef ABS
#define ABS(n) ((n) > 0 ? (n) : -(n))
#endif

#ifndef BITGET
#define BITGET(i, n) ((i) & (1u << (n)))
#endif

#ifndef BITGETS
#define BITGETS_32(i, start, n)         (((uint32_t)(i) >> (start)) & ((1U << (n)) - 1))
#define BITGETS_64(i, start, n)         (((uint64_t)(i) >> (start)) & ((1ULL << (n)) - 1))
#define BITGETS_M_TO_N_32(i, high, low) (((uint32_t)(i) >> (low)) & ((1U << ((high) - (low) + 1)) - 1))
#define BITGETS_M_TO_N_64(i, high, low) (((uint64_t)(i) >> (low)) & ((1ULL << ((high) - (low) + 1)) - 1))
#endif

#ifndef BITSET
#define BITSET(p, n) (*(p) |= (1u << (n)))
#endif

#ifndef BITCLR
#define BITCLR(p, n) (*(p) &= ~(1u << (n)))
#endif

// offsetof(T, m) = 成员 m 在结构体 T 中的起始偏移
#ifndef offsetof
#define offsetof(type, member) \
    ((size_t)(&((type *)0)->member))
#endif

// offsetofend(T, m) = 成员 m 在结构体 T 中的起始偏移 + 成员 m 的大小
#ifndef offsetofend
#define offsetofend(type, member) \
    (offsetof(type, member) + sizeof(((type *)0)->member))
#endif

// container_of(ptr, type, member) = 根据结构体 type 中成员 member 的指针 ptr, 计算出该结构体的起始地址
#ifndef container_of
#define container_of(ptr, type, member) \
    ((type *)((char *)(ptr) - offsetof(type, member)))
#endif

static inline struct timespec time_now(void)
{
    struct timespec ret;
    if (0 != clock_gettime(CLOCK_REALTIME, &ret))
    {
        LOG_PRINT_ERROR("clock_gettime failed, errno[%d](%s)", errno, strerror(errno));
        ret.tv_sec = 0;
        ret.tv_nsec = 0;
    }
    return ret;
}

static inline struct timespec time_mono(void)
{
    struct timespec ret;
    if (0 != clock_gettime(CLOCK_MONOTONIC, &ret))
    {
        LOG_PRINT_ERROR("clock_gettime failed, errno[%d](%s)", errno, strerror(errno));
        ret.tv_sec = 0;
        ret.tv_nsec = 0;
    }
    return ret;
}

static inline struct timespec time_after(uint32_t ms)
{
    struct timespec now = time_now();
    now.tv_sec += ms / 1000;
    now.tv_nsec += (ms % 1000) * 1000000;
    if (now.tv_nsec >= 1000000000)
    {
        now.tv_sec += 1;
        now.tv_nsec -= 1000000000;
    }

    return now;
}

static inline struct timespec time_mono_after(uint32_t ms)
{
    struct timespec now = time_mono();
    now.tv_sec += ms / 1000;
    now.tv_nsec += (ms % 1000) * 1000000;
    if (now.tv_nsec >= 1000000000)
    {
        now.tv_sec += 1;
        now.tv_nsec -= 1000000000;
    }

    return now;
}

static inline bool get_local_timezone(long int *tz)
{
    if (NULL == tz)
    {
        return false;
    }

    time_t t1 = time(NULL);
    time_t t2 = t1;
    struct tm curTime1 = {};
    struct tm curTime2 = {};
    struct tm *localTime = localtime_r(&t1, &curTime1);
    struct tm *gmTime = gmtime_r(&t2, &curTime2);
    if ((localTime == NULL) || (gmTime == NULL))
    {
        return false;
    }

    t1 = mktime(&curTime1);
    t2 = mktime(&curTime2);
    if ((t1 == -1) || (t2 == -1))
    {
        LOG_PRINT_ERROR("mktime current time failed.");
        return false;
    }

    *tz = (t1 - t2) / 3600;

    return true;
}

static inline size_t safe_strnlen(const char *str, size_t maxsize)
{
    if (str == NULL)
    {
        return 0;
    }

    for (size_t i = 0; i < maxsize; ++i)
    {
        if (str[i] == '\0')
        {
            return i;
        }
    }

    return maxsize;
}

#ifndef RAND_MAX
#define RAND_MAX 2147483647
#endif

static inline int get_random_number_from_closed_range(int min, int max)
{
    static unsigned int s_seed = 0;
    assert(max > min);

    if (s_seed == 0)
    {
        s_seed = (unsigned int)time(NULL);
        srand(s_seed);
    }

    int _rand = rand();
    _rand = min + (int)((double)((double)(max) - (min) + 1.0) * ((_rand) / ((RAND_MAX) + 1.0)));
    return _rand;
}

static inline char *random_string(char *buf, int len)
{
    // clang-format off
    static char s_characters[] = {
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
        'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
        'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    };
    // clang-format on
    int i = 0;
    for (; i < len; i++)
    {
        buf[i] = s_characters[get_random_number_from_closed_range(0, sizeof(s_characters) - 1)];
    }
    buf[i] = '\0';
    return buf;
}

int32_t file_write_atomic_sync(const char *filename, const uint8_t *data, size_t len);
int32_t execute_command(const char *cmd);
int32_t get_output_command(const char *cmd, uint8_t *output, size_t output_len);

/**
 * @brief Turn the current process into a daemon.
 *
 * This function forks the current process and detaches it from the controlling
 * terminal to run as a background daemon. The parent process exits immediately,
 * and the child continues as a daemon.
 *
 * On failure (e.g., fork(), chdir(), or open() fails), it returns false and sets errno.
 * On success, it returns true.
 *
 * @note This function has the following side effects:
 *       - The process becomes a child of init (PID changes).
 *       - A new session is created via setsid().
 *       - The current working directory is changed to "/".
 *       - The umask is set to 0.
 *       - Standard file descriptors (stdin, stdout, stderr) are redirected to /dev/null.
 *
 * @return true if daemonization succeeded, false on error (with errno set).
 */
bool daemonize(void);

/**
 * @brief Get the executable filename of a process (basename only).
 *
 * Reads the executable name of the process with given PID by resolving
 * the symbolic link at `/proc/<pid>/exe`. Only the basename (e.g., "sshd")
 * is returned, not the full path.
 *
 * @param[in]  p_pid             Process ID
 * @param[out] p_exec_name       Buffer to store the executable name
 * @param[in]  p_exec_name_maxsize Size of p_exec_name buffer in bytes
 *
 * @return 0 on success, -1 on failure (errno may be set)
 *
 * @note
 *   - Returns the basename of the executable (strips path).
 *   - Result is always null-terminated, even if truncated.
 *   - Depends on `/proc` filesystem (Linux-specific).
 *   - If the executable is deleted or inaccessible, the behavior is implementation-defined.
 */
int32_t get_exec_name(pid_t p_pid, char *p_exec_name, size_t p_exec_name_maxsize);

#ifdef __cplusplus
}
#endif
